// xaudio2_echo.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <xaudio2.h>
#include <xaudio2fx.h>
#include <conio.h>
#include <iostream>
#include <XAPO.h>
#include <XAPOFX.h>

#pragma comment(lib, "XAPOFX.lib");


#ifdef _XBOX //Big-Endian
#define fourccRIFF 'RIFF'
#define fourccDATA 'data'
#define fourccFMT 'fmt '
#define fourccWAVE 'WAVE'
#define fourccXWMA 'XWMA'
#define fourccDPDS 'dpds'
#endif

#ifndef _XBOX //Little-Endian

#define fourccRIFF 'FFIR'
#define fourccDATA 'atad'
#define fourccFMT ' tmf'
#define fourccWAVE 'EVAW'
#define fourccXWMA 'AMWX'
#define fourccDPDS 'sdpd'

#endif

HRESULT FindChunk(HANDLE hFile, DWORD fourcc, DWORD & dwChunkSize, DWORD & dwChunkDataPosition);
HRESULT ReadChunkData(HANDLE hFile, void * buffer, DWORD buffersize, DWORD bufferoffset);
void EchoEffect(BYTE* buffer, int dwChunkSize, float Delay, DWORD nSamplesPerSec);


int _tmain(int argc, _TCHAR* argv[])
{
	WAVEFORMATEXTENSIBLE wfx = {0}; //structure defines the format of waveform-audio data
	XAUDIO2_BUFFER buffer = {0};


	IXAudio2* pXAudio2 = NULL;
	UINT32 deviceCount;
	XAUDIO2_DEVICE_DETAILS deviceDetails;
	int preferredDevice = 0;
	
	CoInitializeEx(NULL, COINIT_MULTITHREADED);

	HRESULT hr;
	if (FAILED(hr = XAudio2Create(&pXAudio2, XAUDIO2_DEBUG_ENGINE, XAUDIO2_DEFAULT_PROCESSOR) ) ) 
		return hr;
	
	
	IXAudio2MasteringVoice* pMasteringVoice = NULL;
    if( FAILED( hr = pXAudio2->CreateMasteringVoice( &pMasteringVoice ) ) )
		return hr;
	
	pXAudio2->GetDeviceCount(&deviceCount);

	
	TCHAR * strFileName = _TEXT("musicmono.wav");

	// Open the file
	HANDLE hFile = CreateFile(
		strFileName,
		GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		0,
		NULL );

	if( INVALID_HANDLE_VALUE == hFile ) 
		return HRESULT_FROM_WIN32( GetLastError() );

	if( INVALID_SET_FILE_POINTER == SetFilePointer( hFile, 0, NULL, FILE_BEGIN ) ) 
		return HRESULT_FROM_WIN32( GetLastError() );

	DWORD dwChunkSize;
	DWORD dwChunkPosition;
	//check the file type, should be fourccWAVE or 'XWMA'
	FindChunk(hFile,fourccRIFF,dwChunkSize, dwChunkPosition );
	DWORD filetype;
	ReadChunkData(hFile,&filetype,sizeof(DWORD),dwChunkPosition);
	if (filetype != fourccWAVE)
		return S_FALSE;
	
	FindChunk(hFile,fourccFMT, dwChunkSize, dwChunkPosition );
	ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition );

	
	//fill out the audio data buffer with the contents of the fourccDATA chunk
	FindChunk(hFile,fourccDATA,dwChunkSize, dwChunkPosition );
	BYTE * pDataBuffer = new BYTE[dwChunkSize];

	ReadChunkData(hFile, pDataBuffer, dwChunkSize, dwChunkPosition);

	
	buffer.AudioBytes = dwChunkSize;  //size of the audio buffer in bytes
	buffer.pAudioData = pDataBuffer;  //buffer containing audio data
	buffer.Flags = XAUDIO2_END_OF_STREAM; // tell the source voice not to expect any data after this buffer
	

	
	float Delay = 4000;
	EchoEffect(pDataBuffer, dwChunkSize, Delay, wfx.Format.nSamplesPerSec);


	IXAudio2SourceVoice* pSourceVoice;
	if( FAILED(hr = pXAudio2->CreateSourceVoice( &pSourceVoice, (WAVEFORMATEX*)&wfx ) ) )  {
			std::cout << "error createsourcevoice";
			getch();
			return hr;
	}



	if( FAILED(hr = pSourceVoice->SubmitSourceBuffer( &buffer ) ) )
	    return hr;

		
	
	if ( FAILED(hr = pSourceVoice->Start( 0, XAUDIO2_COMMIT_NOW ) ) )
    return hr;
	
	

	getch();

	return 0;
}


HRESULT FindChunk(HANDLE hFile, DWORD fourcc, DWORD & dwChunkSize, DWORD & dwChunkDataPosition)
{
    HRESULT hr = S_OK;
    if( INVALID_SET_FILE_POINTER == SetFilePointer( hFile, 0, NULL, FILE_BEGIN ) )
        return HRESULT_FROM_WIN32( GetLastError() );

    DWORD dwChunkType;
    DWORD dwChunkDataSize;
    DWORD dwRIFFDataSize = 0;
    DWORD dwFileType;
    DWORD bytesRead = 0; 
    DWORD dwOffset = 0;

    while (hr == S_OK)
    {
        DWORD dwRead;
        if( 0 == ReadFile( hFile, &dwChunkType, sizeof(DWORD), &dwRead, NULL ) )
            hr = HRESULT_FROM_WIN32( GetLastError() );

        if( 0 == ReadFile( hFile, &dwChunkDataSize, sizeof(DWORD), &dwRead, NULL ) )
            hr = HRESULT_FROM_WIN32( GetLastError() );

        switch (dwChunkType)
        {
        case fourccRIFF:
            dwRIFFDataSize = dwChunkDataSize;
            dwChunkDataSize = 4;
            if( 0 == ReadFile( hFile, &dwFileType, sizeof(DWORD), &dwRead, NULL ) )
                hr = HRESULT_FROM_WIN32( GetLastError() );
            break;

        default:
            if( INVALID_SET_FILE_POINTER == SetFilePointer( hFile, dwChunkDataSize, NULL, FILE_CURRENT ) )
            return HRESULT_FROM_WIN32( GetLastError() );            
        }

        dwOffset += sizeof(DWORD) * 2;
        
        if (dwChunkType == fourcc)
        {
            dwChunkSize = dwChunkDataSize;
            dwChunkDataPosition = dwOffset;
            return S_OK;
        }

        dwOffset += dwChunkDataSize;
        
        if (bytesRead >= dwRIFFDataSize) return S_FALSE;

    }

    
    return S_OK;
    
}

HRESULT ReadChunkData(HANDLE hFile, void * buffer, DWORD buffersize, DWORD bufferoffset)
{
    HRESULT hr = S_OK;
    if( INVALID_SET_FILE_POINTER == SetFilePointer( hFile, bufferoffset, NULL, FILE_BEGIN ) )
        return HRESULT_FROM_WIN32( GetLastError() );
    DWORD dwRead;
    if( 0 == ReadFile( hFile, buffer, buffersize, &dwRead, NULL ) )
        hr = HRESULT_FROM_WIN32( GetLastError() );
    return hr;
}

void EchoEffect(BYTE* buffer, int dwChunkSize, float Delay, DWORD nSamplesPerSec)
{
BYTE* buffer2 = new BYTE[dwChunkSize];

int ChunkNumber = Delay * nSamplesPerSec / 1000;


memcpy(buffer2, buffer, dwChunkSize);

for (int i = ChunkNumber; i <= dwChunkSize - 1; i++) {
	int buf = buffer2[i - ChunkNumber] + buffer[i];
	if (buffer2[i - ChunkNumber] <= 127 && buffer[i] <= 127 && buf > 127) 
		buffer[i] = 127;
	else
		if ( buffer2[i - ChunkNumber] > 127 && buffer[i] > 127) {
			int buf2 = ~buffer2[i - ChunkNumber] + ~buffer[i] + 2;
			if (buf2 <= 128) {
				buffer[i] = buf2;
				buffer[i] = ~buffer[i] + 1;
			}
			else
				buffer[i] = 128;
		}
		else
			buffer[i] += buffer2[i - ChunkNumber];

}
return;
}
	
	